/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma  once

#include <string>
#include <stdint.h>
#include <hadoop/Error.h>
#include <QtCore/QIODevice>

namespace HadoopUtils {

	
	/**
	* Check to make sure that the condition is true, and throw an exception
	* if it is not. The exception will contain the message and a description
	* of the source location.
	*/
#define HADOOP_ASSERT(CONDITION, MESSAGE) \
	{ \
	if (!(CONDITION)) { \
	throw HadoopUtils::Error((MESSAGE), __FILE__, __LINE__, \
	__FUNCTION__); \
	} \
	}

	/**
	* An interface for an input stream.
	*/
	class InStream {
	public:
		/**
		* Reads len bytes from the stream into the buffer.
		* @param buf the buffer to read into
		* @param buflen the length of the buffer
		* @throws Error if there are problems reading
		*/
		virtual void read(void *buf, size_t len) = 0;
		virtual ~InStream() {}
	};

	/**
	* An interface for an output stream.
	*/
	class OutStream {
	public:
		/**
		* Write the given buffer to the stream.
		* @param buf the data to write
		* @param len the number of bytes to write
		* @throws Error if there are problems writing
		*/
		virtual void write(const void *buf, size_t len) = 0;
		/**
		* Flush the data to the underlying store.
		*/
		virtual void flush() = 0;
		virtual ~OutStream() {}
	};

	/**
	* A class to read a file as a stream.
	*/
	class FileInStream : public InStream {
	public:
		FileInStream();
		bool open(const std::string& name);
		bool open(QIODevice* file);
		void read(void *buf, size_t buflen);
		bool skip(size_t nbytes);
		bool close();
		virtual ~FileInStream();
	private:
		/**
		* The file to write to.
		*/
		QIODevice *mFile;
		/**
		* Does is this class responsible for closing the FILE*?
		*/
		bool isOwned;
	};

	/**
	* A class to write a stream to a file.
	*/
	class FileOutStream: public OutStream {
	public:

		/**
		* Create a stream that isn't bound to anything.
		*/
		FileOutStream();

		/**
		* Create the given file, potentially overwriting an existing file.
		*/
		bool open(const std::string& name, bool overwrite);
		bool open(QIODevice* file);
		void write(const void* buf, size_t len);
		bool advance(size_t nbytes);
		void flush();
		bool close();
		virtual ~FileOutStream();
	private:
		QIODevice *mFile;
		bool isOwned;
	};

	/**
	* A stream that reads from a string.
	*/
	class StringInStream: public InStream {
	public:
		StringInStream(const std::string& str);
		virtual void read(void *buf, size_t buflen);
	private:
		std::string buffer;
		std::string::const_iterator itr;
	};


	class StringOutStream:public OutStream {
	public:
		/**
		* Write the given buffer to the stream.
		* @param buf the data to write
		* @param len the number of bytes to write
		* @throws Error if there are problems writing
		*/
		virtual void write(const void *buf, size_t len) ;
		/**
		* Flush the data to the underlying store.
		*/
		virtual void flush() {};
		virtual ~StringOutStream() {}
		std::string str();
	private:
		std::string data;
	};

	void serializeInt(int32_t t, OutStream& stream);
	int32_t deserializeInt(InStream& stream);
	void serializeLong(int64_t t, OutStream& stream);
	int64_t deserializeLong(InStream& stream);
	void serializeFloat(float t, OutStream& stream);
	float deserializeFloat(InStream& stream);
	void serializeString(const std::string& t, OutStream& stream);
	void deserializeString(std::string& t, InStream& stream);
}

